package ma.fsm.arda.command;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.sintef.jarduino.AnalogPin;
import org.sintef.jarduino.AnalogReference;
import org.sintef.jarduino.DigitalPin;
import org.sintef.jarduino.DigitalState;
import org.sintef.jarduino.FixedSizePacket;
import org.sintef.jarduino.InterruptPin;
import org.sintef.jarduino.InterruptTrigger;
import org.sintef.jarduino.JArduinoMessageHandler;
import org.sintef.jarduino.JArduinoProtocol;
import org.sintef.jarduino.JArduinoProtocolPacket;
import org.sintef.jarduino.PWMPin;
import org.sintef.jarduino.PinMode;
import org.sintef.jarduino.comm.Serial4JArduino;
import org.sintef.jarduino.msg.AnalogReadResultMsg;
import org.sintef.jarduino.msg.DigitalReadResultMsg;
import org.sintef.jarduino.msg.Eeprom_valueMsg;
import org.sintef.jarduino.msg.Eeprom_write_ackMsg;
import org.sintef.jarduino.msg.InterruptNotificationMsg;
import org.sintef.jarduino.msg.PongMsg;
import org.sintef.jarduino.observer.JArduinoClientObserver;
import org.sintef.jarduino.observer.JArduinoClientSubject;
import org.sintef.jarduino.observer.JArduinoObserver;

public class JarduinoDataController extends JArduinoMessageHandler implements
		JArduinoObserver {

	private JArduinoClientObserver device;
	private SimpleDateFormat dateFormat;
	private short analogRead_result;
	private boolean analogRead_result_available;
	private final Object analogReadMonitor = "analogReadMonitor";

	public JarduinoDataController(JArduinoClientObserver device) {
		this.device = device;
		dateFormat = new SimpleDateFormat("dd MMM yyy 'at' HH:mm:ss.SSS");
	}

	private void doSend(FixedSizePacket data) {
		if (data != null) {
			System.out.println(data + " --> " + data.getPacket());

			device.receiveMsg(data.getPacket());

		} else {
			System.out.println("Data is null");
		}
	}

	public final void sendpinMode(PinMode mode, DigitalPin pin) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createPinMode(pin, mode);
		doSend(fsp);
	}

	public final void senddigitalRead(DigitalPin pin) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createDigitalRead(pin);
		doSend(fsp);
	}

	public final void senddigitalWrite(DigitalPin pin, DigitalState value) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createDigitalWrite(pin, value);
		doSend(fsp);
	}

	public final void sendanalogReference(AnalogReference type) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createAnalogReference(type);
		doSend(fsp);
	}

	public final short sendanalogRead(AnalogPin pin) {
		try {
			synchronized (analogReadMonitor) {
				analogRead_result_available = false;
				FixedSizePacket fsp = JArduinoProtocol.createAnalogRead(pin);
				doSend(fsp);
				analogReadMonitor.wait(500);
				if (analogRead_result_available) //
					System.out.print("USER RESULT : ");
				System.out.println(analogRead_result);
				return analogRead_result;
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.err
				.println("JArduino: Timeout waiting for the result of analogRead");
		return 0;

	}

	public final void sendanalogWrite(PWMPin pin, byte value) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createAnalogWrite(pin, value);
		doSend(fsp);
	}

	public final void sendtone(DigitalPin pin, int freq, int dur) {
		FixedSizePacket fsp = null;
		Short frequency = (short) freq;
		Short duration = (short) dur;
		fsp = JArduinoProtocol.createTone(pin, frequency, duration);
		doSend(fsp);
	}

	public final void sendnoTone(DigitalPin pin) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createNoTone(pin);
		doSend(fsp);
	}

	public final void sendping() {
		doSend(JArduinoProtocol.createPing());
	}

	public final void sendattachInterrupt(InterruptPin interrupt,
			InterruptTrigger mode) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createAttachInterrupt(interrupt, mode);
		doSend(fsp);
	}

	public final void senddetachInterrupt(InterruptPin interrupt) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createDetachInterrupt(interrupt);
		doSend(fsp);
	}

	public final void sendeeprom_read(Short address) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createEeprom_read(address);
		doSend(fsp);
	}

	public final void sendeeprom_sync_write(Short address, Byte value) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createEeprom_sync_write(address, value);
		doSend(fsp);
	}

	public final void sendeeprom_write(Short address, Byte value) {
		FixedSizePacket fsp = null;
		fsp = JArduinoProtocol.createEeprom_write(address, value);
		doSend(fsp);
	}

	public final void receiveMessage(byte[] packet) {
		FixedSizePacket data = JArduinoProtocol.createMessageFromPacket(packet);

		if (data != null) {
			System.out.println(" ["
					+ dateFormat.format(new Date(System.currentTimeMillis()))
					+ "]: " + data.toString() + " --> "
					+ FixedSizePacket.toString(packet));
			// TODO Add
		}
	}

	public JArduinoClientObserver getDevice() {
		return device;
	}

	public void setDevice(JArduinoClientObserver device) {
		this.device = device;
	}

	@Override
	// Messages from the JArduino device arrive here
	public void receiveMsg(byte[] msg) {
		JArduinoProtocolPacket p = (JArduinoProtocolPacket) JArduinoProtocol
				.createMessageFromPacket(msg);
		p.acceptHandler(this);
	}

	@Override
	public void handleDigitalReadResult(DigitalReadResultMsg msg) {
		// digitalRead_result = msg.getValue();
		// digitalRead_result_available = true;
		// synchronized (digitalReadMonitor) {
		// digitalReadMonitor.notify();
		// }
	}

	@Override
	public void handleAnalogReadResult(AnalogReadResultMsg msg) {
		analogRead_result = msg.getValue();
		analogRead_result_available = true;
		synchronized (analogReadMonitor) {
			analogReadMonitor.notify();
		}
	}

	@Override
	public void handleEeprom_value(Eeprom_valueMsg msg) {

	}

	@Override
	public void handlePong(PongMsg msg) {

	}

	@Override
	public void handleEeprom_write_ack(Eeprom_write_ackMsg msg) {

	}

	@Override
	public void handleInterruptNotification(InterruptNotificationMsg msg) {

	}
}
